package fileReader.demo.reader;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;


/**
 * This class is resposible for reading ESRI maps 
 * and storing data into Map object.
 */
public class EsriFileReader {

	LayerReader utilities=new LayerReader();
	private Map map;
	private String layerName="";
	private PolygonMapLayer deptharea=new PolygonMapLayer();
	private PolygonMapLayer generarea=new PolygonMapLayer();
	private PolygonMapLayer fwayarea=new PolygonMapLayer();
	private PolygonMapLayer building=new PolygonMapLayer();
	private PolygonMapLayer builtupare=new PolygonMapLayer();
	private PolyLineMapLayer depthcontour=new PolyLineMapLayer();
	private PolyLineMapLayer coastline=new PolyLineMapLayer();
	private PolyLineMapLayer navigline=new PolyLineMapLayer();
	private PolyLineMapLayer pipeline=new PolyLineMapLayer();
	private PolyLineMapLayer limitl=new PolyLineMapLayer();
	private PolyLineMapLayer river=new PolyLineMapLayer();
	private PolyLineMapLayer roadnrailw=new PolyLineMapLayer();
	private PolyLineMapLayer bridge=new PolyLineMapLayer();
	private PolyLineMapLayer leadinglne=new PolyLineMapLayer();
	private PointMapLayer rock=new PointMapLayer();
	private PointMapLayer text=new PointMapLayer();
	private PointMapLayer limitp=new PointMapLayer();
	private PointMapLayer obstruct=new PointMapLayer();
	private PointMapLayer harbour=new PointMapLayer();
	private PointMapLayer natseabed=new PointMapLayer();	
	private PointMapLayer signsound=new PointMapLayer();
	
	PointReader pointReader = new PointReader();
	PolygonReader polygonReader = new PolygonReader();
	PolyLineReader polylineReader = new PolyLineReader();
	PointZReader pointzReader = new PointZReader();
	/**
	 * This method is resposible for initiating the reading of
	 * layers specified in settins file.
	 * 
	 * @param folderPath folder path in which specified files should be found
	 * @param map data all the data read from files is stored here
	 */
	public void readMap(String folderPath, Map map)
	{
		
		this.map=map;
		//try 
		//{
			Vector toRead = new Vector();
			toRead.add("signsound_p.shp");
			toRead.add("deptharea_r.shp");
			toRead.add("depthcont_l.shp");
			toRead.add("rock_p.shp"); 
			toRead.add("coastline_l.shp");
			toRead.add("ctext_p.shp");
			toRead.add("generarea_r.shp");
			toRead.add("navigline_l.shp");
			toRead.add("pipeline_l.shp");
			toRead.add("limit_l.shp"); 								
			toRead.add("limit_p.shp");
			toRead.add("river_l.shp");
			toRead.add("fwayarea_r.shp");
			toRead.add("roadnrailw_l.shp");
			toRead.add("obstruct_p.shp");
			toRead.add("harbour_p.shp");
			toRead.add("natseabed_p.shp");
			toRead.add("bridge_l.shp");
			toRead.add("leadinglne_l.shp");
			toRead.add("building_r.shp");
			toRead.add("builtupare_r.shp");
			/*
			File settingsFile = new File("fileReader\\demo\\reader\\settings.dat");
			String line = null;
			
			BufferedReader input = new BufferedReader(new FileReader(settingsFile));
			while (( line = input.readLine()) != null)
			{
		        toRead.add(line);
			}
			*/
		    for(int i=0; i<toRead.size(); i++)
		    {
		    	layerName=(String)toRead.get(i);
		    	String filePath = null;
		    	filePath = folderPath.concat((String)toRead.get(i));
		    	File mapFile = new File(filePath);
		    	readFile(mapFile);
		    	layerName="";

			}
		/*
		}
		catch (FileNotFoundException e) {
			Simulator.errorMsg("File not found");
		}
		catch (IOException ex){
			Simulator.errorMsg("Error while opening file.");
		    }*/
	
	}
	
	/**
	 * This method runs reading of map layer. Data from main file (.shp)
	 * and attributes from database file (.dbf) are stored to right type of map layer. 
	 * 
	 * @param file file to read
	 */
	public void readFile(File file){
		
		try {
			FileInputStream streamIn = new FileInputStream(file.getPath());
			DataInputStream dataIn = new DataInputStream(streamIn);
			
			//MapLayer layer = readShp(dataIn);
			String layer = readShp(dataIn);
			
			String dbfPath =  file.getPath();
			dbfPath = dbfPath.replace("shp", "dbf");
			
			FileInputStream streamIn2 = new FileInputStream(dbfPath);
			DataInputStream dataIn2 = new DataInputStream(streamIn2);
			
			readDbfHeader(dataIn2, layer);
			
			dataIn.close();
			dataIn2.close();
			
		} catch (FileNotFoundException e) {
			Simulator.errorMsg("File not found.");

		}catch (IOException e) {
			Simulator.errorMsg("Error while opening file.");
		}
		
	}
	
	/**
	 * Method that reads data from main file (.shp).
	 * Method is responsible for using right type of reader
	 * and storing data into a right type of map layer.
	 * 
	 * @param dataIn data input stream for file reading
	 * @return MapLayer what kind of maplayer shape is.
	 */
	private String readShp(DataInputStream dataIn){
		
		try {
			
			String layer = "";
			dataIn.readInt();//9994
			dataIn.skipBytes(20);
			int fileLength = 2 * (dataIn.readInt());//file length converted to length in Bytes
			
			utilities.readLittleInteger(dataIn.readInt()); //1000
			int shapeType = utilities.readLittleInteger(dataIn.readInt()); //0-31
			
			//Box boxXY = readBoxXY(dataIn);
			//Box boxZM = readBoxZM(dataIn);
			
						
			Box boxXY = new Box();
			Box boxZM = new Box();
			
			boxXY.setXMin(utilities.readLittleDouble(dataIn.readDouble()));
			boxXY.setYMin(utilities.readLittleDouble(dataIn.readDouble()));
			boxXY.setXMax(utilities.readLittleDouble(dataIn.readDouble()));
			boxXY.setYMax(utilities.readLittleDouble(dataIn.readDouble()));
			boxZM.setXMin(utilities.readLittleDouble(dataIn.readDouble()));
			boxZM.setYMin(utilities.readLittleDouble(dataIn.readDouble()));
			boxZM.setXMax(utilities.readLittleDouble(dataIn.readDouble()));
			boxZM.setYMax(utilities.readLittleDouble(dataIn.readDouble()));
			
			if(boxXY.getXMin()<MapLayer.getGlobalBoundingBox().getXMin())
			{
				MapLayer.getGlobalBoundingBox().setXMin(boxXY.getXMin());
			}
			
			if(boxXY.getYMin()<MapLayer.getGlobalBoundingBox().getYMin())
			{
				MapLayer.getGlobalBoundingBox().setYMin(boxXY.getYMin());
			}
			
			if(boxXY.getXMax()>MapLayer.getGlobalBoundingBox().getXMax())
			{
				MapLayer.getGlobalBoundingBox().setXMax(boxXY.getXMax());
			}
			
			if(boxXY.getYMax()>MapLayer.getGlobalBoundingBox().getYMax())
			{
				MapLayer.getGlobalBoundingBox().setYMax(boxXY.getYMax());
			}
			
			switch(shapeType)
			{
				case 1:		
					if(layerName.contains("rock_p"))
						layer=readPointShape("rock", rock, dataIn, fileLength);
					if(layerName.contains("ctext_p"))
						layer=readPointShape("text", text, dataIn, fileLength);
					if(layerName.contains("limit_p"))
						layer = readPointShape("limitp", limitp, dataIn, fileLength);
					if(layerName.contains("obstruct_p"))
						layer = readPointShape("obstruct", obstruct, dataIn, fileLength);
					if(layerName.contains("harbour_p"))
						layer = readPointShape("harbour", harbour, dataIn, fileLength);
					if(layerName.contains("natseabed_p"))
						layer = readPointShape("natseabed", natseabed, dataIn, fileLength);					
					break;
				
				
				case 3: 	
					if(layerName.contains("depthcont_l")){
						layer = readPolyLineShape("depthcontour", depthcontour, dataIn, fileLength);
						depthcontour.setBoundingBoxXY(boxXY);
						depthcontour.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("coastline")){
						layer = readPolyLineShape("coastline", coastline, dataIn, fileLength);
						coastline.setBoundingBoxXY(boxXY);
						coastline.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("navigline_l")){
						layer = readPolyLineShape("navigline", navigline, dataIn, fileLength);
						navigline.setBoundingBoxXY(boxXY);
						navigline.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("pipeline_l")){
						layer = readPolyLineShape("pipeline", pipeline, dataIn, fileLength);
						pipeline.setBoundingBoxXY(boxXY);
						pipeline.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("limit_l")){
						layer = readPolyLineShape("limitl", limitl, dataIn, fileLength);
						limitl.setBoundingBoxXY(boxXY);
						limitl.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("river_l")){
						layer = readPolyLineShape("river", river, dataIn, fileLength);
						river.setBoundingBoxXY(boxXY);
						river.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("roadnrailw_l")){
						layer = readPolyLineShape("roadnrailw", roadnrailw, dataIn, fileLength);
						roadnrailw.setBoundingBoxXY(boxXY);
						roadnrailw.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("bridge_l")){
						layer = readPolyLineShape("bridge", bridge, dataIn, fileLength);
						bridge.setBoundingBoxXY(boxXY);
						bridge.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("leadinglne_l")){
						layer = readPolyLineShape("leadingline", leadinglne, dataIn, fileLength);
						leadinglne.setBoundingBoxXY(boxXY);
						leadinglne.setBoundingBoxZM(boxZM);
					}
					break;
					
				case 5:
					if(layerName.contains("deptharea_r")){
						layer = readPolygonShape("deptharea", deptharea, dataIn, fileLength);
						deptharea.setBoundingBoxXY(boxXY);
						deptharea.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("generarea_r")){
						layer = readPolygonShape("generarea", generarea, dataIn, fileLength);
						generarea.setBoundingBoxXY(boxXY);
						generarea.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("fwayarea_r")){
						layer = readPolygonShape("fwayarea", fwayarea, dataIn, fileLength);
						fwayarea.setBoundingBoxXY(boxXY);
						fwayarea.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("building_r")){
						layer = readPolygonShape("building", building, dataIn, fileLength);
						building.setBoundingBoxXY(boxXY);
						building.setBoundingBoxZM(boxZM);
					}else if(layerName.contains("builtupare_r")){
						layer = readPolygonShape("builtupare", builtupare, dataIn, fileLength);
						builtupare.setBoundingBoxXY(boxXY);
						builtupare.setBoundingBoxZM(boxZM);
					}
					break;
							
				case 11:
					layer = readPointZShape("signsound", signsound, dataIn, fileLength);
				break;
			}
						
			return layer;
		
			
		} catch (IOException e) {
			Simulator.errorMsg("Error while opening file in EsriFileReader/readShp.");
		}
		
		return null;
	}

	/**
	 * Method reads only dbf header data
	 * Only number of records is meaningful for us, all the other data can be skipped
	 * 
	 * @param dataIn data inputstream for reading file.
	 * @param layer map layer in which data is stored
	 */
	
	private void readDbfHeader(DataInputStream dataIn, String layer){

		try {
			boolean stupidType = false;
	
			dataIn.readByte(); 							//dbf-file version
			dataIn.readByte();	// YYYY+1900
			dataIn.readByte();							// MM
			dataIn.readByte();							// DD
			int numberOfRecords=utilities.readLittleInteger(dataIn.readInt());	// number of records
			utilities.readLittleShort(dataIn.readShort());  // number of bytes in the header
			utilities.readLittleShort(dataIn.readShort());		// number of bytes in the record
			dataIn.skipBytes(20);						// read 20 Bytes forward
			
			Vector descriptors = new Vector();
			char value = (char)dataIn.readByte();		// check if terminator
			
			while( value != 13 )
			{	
				
				String desc = "";
				desc = desc + value;
				for(int c=0; c<10; c++)
				{
					desc = desc + (char)dataIn.readByte();
				}
				descriptors.add(desc);						// descriptor header
				
				dataIn.readByte();	// fieldtype
				dataIn.skipBytes(4);						// data field address
				dataIn.readByte();	// field length
				dataIn.skipBytes(15);						// read 15 bytes forward
				
				value = (char)dataIn.readByte(); 			// check for terminator
			}

			// quick bad fix for differences between maps
			if(descriptors.indexOf(descriptors.lastElement())>13 && descriptors.get(14).toString().contains("DATEAC"))	
			{
				stupidType = true;
			}

			readDbfContent(dataIn, layer, numberOfRecords, stupidType);
		} catch (IOException e) {
			Simulator.errorMsg("Error while opening filein EsriFileReader/readDbfHeader.");
			//e.printStackTrace();
		}

}

	
	/**
	 * Method that reads attributes from database file (.dbf).
	 * 
	 * @param dataIn data input stream for reading a file
	 * @param layer map layer where attributes are stored
	 * @param numOfRecords number of records to read from a file.
	 */
	private void readDbfContent(DataInputStream dataIn, String layer, int numOfRecords, boolean stupidType){
		
		if( layer.equals("depthcontour"))
		{
			polylineReader.readDepthContourAttributes(dataIn, depthcontour, numOfRecords);
		}
		else if( layer.equals("deptharea"))
		{
			polygonReader.readDepthAreaAttributes(dataIn, deptharea, numOfRecords);
		}
		else if( layer.equals("signsound"))
		{
			PointZReader.setStupidType(stupidType);
			pointzReader.readSignSoundAttributes(dataIn, signsound, numOfRecords);
		}
		else if( layer.equals("rock"))
		{
			pointReader.readRockAttributes(dataIn, rock, numOfRecords);
		}
		else if( layer.equals("coastline"))
		{
			polylineReader.readCoastLineAttributes(dataIn, numOfRecords);
		}
		else if( layer.equals("text"))
		{
			pointReader.readTextAttributes(dataIn, text, numOfRecords);
		}
		else if( layer.equals("generarea"))
		{
			polygonReader.readGenerAreaAttributes(dataIn, generarea, numOfRecords);
		}
		else if( layer.equals("navigline"))
		{
			polylineReader.readNavigLineAttributes(dataIn, navigline, numOfRecords);
		}
		else if( layer.equals("pipeline"))
		{
			polylineReader.readPipeLineAttributes(dataIn, pipeline, numOfRecords);
		}
		else if( layer.equals("limitl"))
		{
			polylineReader.readLimitLAttributes(dataIn, limitl, numOfRecords);
		}
		else if( layer.equals("limitp"))
		{
			pointReader.readLimitPAttributes(dataIn, limitp, numOfRecords);
		}
		else if( layer.equals("fwayarea"))
		{
			polygonReader.readFwayAttributes(dataIn, fwayarea, numOfRecords);
		}
		else if( layer.equals("river"))
		{
			polylineReader.readRiverAttributes(dataIn, river, numOfRecords);
		}
		else if( layer.equals("natseabed"))
		{
			pointReader.readNatSeaBedAttributes(dataIn, natseabed, numOfRecords);
		}
		else if( layer.equals("obstruct"))
		{
			pointReader.readObstructAttributes(dataIn, obstruct, numOfRecords);
		}
		else if( layer.equals("harbour"))
		{
			pointReader.readHarbourAttributes(dataIn, harbour, numOfRecords);
		}
		else if( layer.equals("roadnrailw"))
		{
			polylineReader.readRoadNRailwAttributes(dataIn, roadnrailw, numOfRecords);
		}
		else if( layer.equals("bridge"))
		{
			polylineReader.readBridgeAttributes(dataIn, bridge, numOfRecords);
		}
		else if( layer.equals("leadinglne"))
		{
			polylineReader.readLeadingLneAttributes(dataIn, leadinglne, numOfRecords);
		}
		else if( layer.equals("building"))
		{
			polygonReader.readBuildingAttributes(dataIn, building, numOfRecords);
		}
		else if( layer.equals("builtupare"))
		{
			polygonReader.readBuiltupareAttributes(dataIn, builtupare, numOfRecords);
		}
	}
	
	//pointMapLayers
	private String readPointShape(String layerType, PointMapLayer layer, DataInputStream dataIn, int mainfileLength){
		pointReader.readPointData(dataIn, layerType, layer, mainfileLength);
		map.setPointMapLayer(layer, layerType.toString());
		return layerType;
	}
	
//	polygonMapLayers
	private String readPolygonShape(String layerType, PolygonMapLayer layer, DataInputStream dataIn, int mainfileLength){
		polygonReader.readPolygonData(dataIn, layerType, layer, mainfileLength);
		map.setPolygonMapLayer(layer, layerType.toString());
		return layerType;
	}
	
//	polygonMapLayers
	private String readPolyLineShape(String layerType, PolyLineMapLayer layer, DataInputStream dataIn, int mainfileLength){
		polylineReader.readPolyLineData(dataIn, layerType, layer, mainfileLength);
		map.setPolyLineMapLayer(layer, layerType.toString());
		return layerType;
	}
	
//	polygonMapLayers
	private String readPointZShape(String layerType, PointMapLayer layer, DataInputStream dataIn, int mainfileLength){
		pointzReader.readPointZData(dataIn, layerType, layer, mainfileLength);
		map.setPointMapLayer(layer, layerType.toString());
		return layerType;
	}
	
	
}